#coding=utf-8

# establish connection
import os
import random
import json

#http服务器的端口和websockets监听的端口。
httpPort = '9580'
wsPort = '9575'

#关掉占用了wsPort的进程
def killport(port):
    command="kill -9 $(netstat -nlp | grep :"+str(port)+" | awk '{print $7}' | awk -F'/' '{{ print $1 }}')"
    os.system(command)
if os.name == 'posix':
    killport(wsPort)
    killport(httpPort)

#获取服务端IP地址，windows系统使用socket直接读取，linux系统使用os调用ip addr命令，并用grep和awk过滤提取显示的IP地址
localIp = []
if os.name == 'nt':
    import socket
    localIp.append(socket.gethostbyname(socket.gethostname()))
elif os.name == 'posix':
    IPs = os.popen("ip addr | grep 'inet '|awk '{print $2}'|awk -F'/' '{print $1}'").readlines()
    for ip in IPs:
        if not '127.0.0.1' in ip:
            localIp.append(ip.strip('\n'))


import asyncio
import websockets
import workmode
#记录服务器运行状态，等待waiting，运行running，暂停paused，停止stopped
serverStatus = {'stat':'waiting','command':'','msg':''} 

#socket服务器向前台发送最简单的消息，
#默认为text，处理方式为直接显示
#para，会根据value的内容改变前台的参数
#btr，发送电量和是否充电的状态
#data，idvt，转移曲线和电流随时间变化的曲线
#clean,会清楚消息框的内容，然后显示消息
async def sendmessage(websocket,msg,type='text'):
    resp = {}
    resp["type"] = type
    resp["value"] = msg
    await websocket.send(json.dumps(resp))

saveContent = ''
def saveData(log):
    from datetime import datetime
    now = datetime.now()
    
    fn = './data/'+now.strftime(r"%Y-%m-%d_%H-%M-%S")+'.txt'
    f = open(fn,'w')
    f.write(log+'------------------------------\n'+saveContent)
    f.close()
    return fn+'已保存'

#执行模块，根据serverStatus的修改进行不同的操作
#执行模块与控制模块异步执行，同时运行，通过serverStatus互相通讯实现实时控制。
async def mainthread(websocket):
    import numpy as np
    import asyncio
    
    #global解决外部变量函数内无法读取的问题
    global serverStatus  

    while True:
        message = ''
        #如果serverStatus.stat处于waiting状态则无限循环
        #一旦被修改为running则会根据msg的类型执行操作
        while True:
            await asyncio.sleep(0.05)
            if serverStatus['stat'] == 'running':
                message = serverStatus['command']
                break     
        #功能选择   
        if message in workmode.allmode.keys():
            global saveContent
            saveContent = ''
            wMode = workmode.allmode[message]
            if 'cycle' in wMode.__dict__.keys() and 'c' in wMode.__dict__.keys():
                if wMode.c == None:
                    wMode.c = wMode.cycle
            await sendmessage(websocket,wMode.eChartsSet,'echarts')
            await wMode.prepare(websocket)
            await sendmessage(websocket,wMode.xCounts,'xcounts')
            for ip in localIp:
                await sendmessage(websocket,ip+':'+httpPort)
            while wMode.isContinue():
                #监测到stat变为stopped则停止循环，并且修改stat为waiting
                if serverStatus['stat'] == 'stopped':
                    serverStatus['stat'] = 'waiting'
                    break
                if serverStatus['command'] == 'tare' and message == '#idvt':                    
                    tareI = await wMode.loopBody(websocket,n='tare')
                    saveContent += '---------------tare:'+ tareI +'------------------\n'
                    serverStatus['command'] = ''
                #发送json形式的数据，如果是测试环境，产生的是0-1之间的随机数。
                resp = {}
                resp["type"] = "data"
                resp["value"] = await wMode.loopBody(websocket)
                listValue = eval(resp["value"])
                for val in listValue:
                    saveContent += '{} '.format(val)
                saveContent += '\n'
                await websocket.send(json.dumps(resp))
                wMode.afterLoop()
                #stat被修改为paused时，进入循环等待，直到stat被修改为running或者stopped
                if serverStatus['stat'] == 'paused':
                    while True:
                        await asyncio.sleep(0.05)
                        if serverStatus['stat'] == 'running':
                            break
                        if serverStatus['stat'] == 'stopped':
                            serverStatus['stat'] == 'waiting'
                            break
            await wMode.endLoop(websocket)            
            await sendmessage(websocket,saveData(serverStatus['msg']))
            if 'c' in wMode.__dict__.keys():
                if wMode.c != None:
                    wMode.c -= 1
                    await sendmessage(websocket,'剩余循环次数：{}'.format(wMode.c))
                    if wMode.c == 0:
                        serverStatus['stat'] = 'waiting'
                        serverStatus['msg'] = ''
                        wMode.c = None 
                else:
                    serverStatus['stat'] = 'waiting'
                    serverStatus['msg'] = ''                    
            else:
                serverStatus['stat'] = 'waiting'
                serverStatus['msg'] = ''
        else:
            await sendmessage(websocket,message)
            serverStatus['stat'] = 'waiting'

#控制模块，与执行模块一起运行。
#主要负责接收前台发送的命令，并进行处理。
#以修改serverStatus的方式控制执行模块的运行方式。
async def controlthread(websocket):
    global serverStatus
    while True:
        req = json.loads(await websocket.recv())
        message = req['command']
        if message == '#stop':
            serverStatus['stat'] = 'stopped'
            serverStatus['command'] = ''
            serverStatus['msg'] = req['msg']
        elif message == '#pause':
            serverStatus['stat'] = 'paused'
            serverStatus['command'] = ''
            serverStatus['msg'] = ''
        elif message == '#continue':
            serverStatus['stat'] = 'running'
            serverStatus['command'] = ''
            serverStatus['msg'] = ''
        elif message == '#tare':
            serverStatus['command'] = 'tare'
            serverStatus['msg'] = ''
        elif message == '#save':
            await sendmessage(websocket,saveData(req['msg']))
        #设置参数：将参数设置为前端传来的参数数据
        elif message == '#set':
            para = req['msg']
            workmode.modeConfig('set',eval(para))
            workmode.savepara()
            await sendmessage(websocket, '参数保存成功！')
        #回复默认参数，并把默认参数传回前端
        elif message == '#default':
            workmode.modeConfig('default')            
            await sendmessage(websocket,workmode.modeConfig(),'para')
            workmode.savepara()
            await sendmessage(websocket,'恢复默认参数！')
        else:
            serverStatus['stat'] = 'running'
            serverStatus['command'] = message
            serverStatus['msg'] = req['msg']

#发送电量和充电状态，与两个模块同事运行，如果在测试环境下，则发送一个随机产生的电量。
async def buttery(websocket):
    from workmode import envTest
    if not envTest:
        import INA219 as ina
        hat=ina.INA219(addr=0x42)
    p = 50
    while True:
        if not envTest:
            busv=hat.getBusVoltage_V()
            current = hat.getCurrent_mA()/1000
            p = (busv - 6)/2.15*100
            if(p > 100):p = 100
            if(p < 0):p = 0
            btr = {'isCharging': current+0.2 > 0, 'percent':f'{p:3.1f}%'}
        else:
            p += random.random()*10-5
            if(p > 100):p = 100
            if(p < 0):p = 0
            btr = {'isCharging': random.random() > 0.5, 'percent':f'{p:3.1f}%'}
        await sendmessage(websocket, btr, 'btr')
        await asyncio.sleep(5 if envTest else 2)
    
#基本的回调函数，通过传递websocket实现各种通讯功能。
async def echo(websocket, path):
    #连接成功后发送IP，以及当前保存的参数。
    for ip in localIp:
        await sendmessage(websocket,ip+':'+httpPort)
    if workmode.envTest:
        await sendmessage(websocket,'当前为测试环境！请将workmode中的envTest变量改为False进入生产环境。')
    workmode.loadpara()
    await sendmessage(websocket,workmode.modeConfig(),'para')
    await asyncio.gather(mainthread(websocket),controlthread(websocket),buttery(websocket))

#ws服务器实体
def wsServer():
    print('Data Collection Server Start!')
    print('---------------------------')
    
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(websockets.serve(echo, '0.0.0.0', wsPort))
    loop.run_forever()

import bottle
from bottle import static_file
import os

rt = os.getcwd()

@bottle.route('/')
def starfile():
    return static_file('index.html', root=rt)

@bottle.route('/<path:path>')
def starfile(path):
    return static_file(path, root=rt)

#bottle-http服务器实体
def httpServer():
    bottle.run(server="paste", host='0.0.0.0', port=httpPort)

import threading
#使用多线程让二者同时运行
t1 = threading.Thread(target=wsServer)
t2 = threading.Thread(target=httpServer)
t1.start()
t2.start()
t1.join()
t2.join()

